package cn.virens;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;

import cn.virens.common.CloseableUtil;
import io.lettuce.core.codec.RedisCodec;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;

public class RedisCode implements RedisCodec<String, Serializable> {
	private static final Charset charset = Charset.forName("UTF-8");
	private static final ByteBuffer EMPTY = ByteBuffer.wrap(new byte[0]);

	@Override
	public String decodeKey(ByteBuffer bytes) {
		if (bytes == null) return null;

		return new String(getBytes(bytes), charset);
	}

	@Override
	public Serializable decodeValue(ByteBuffer bytes) {
		if (bytes == null) return null;

		ByteArrayInputStream byteArrayInputStream = null;
		ObjectInputStream objectInputStream = null;

		try {
			byteArrayInputStream = new ByteArrayInputStream(getBytes(bytes));
			objectInputStream = new ObjectInputStream(byteArrayInputStream);

			Object obj = objectInputStream.readObject();

			if (obj instanceof Serializable) {
				return (Serializable) obj;
			} else {
				return null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			CloseableUtil.close(objectInputStream);
			CloseableUtil.close(byteArrayInputStream);
		}
		return null;
	}

	@Override
	public ByteBuffer encodeKey(String key) {
		if (key == null) return EMPTY;

		return ByteBuffer.wrap(key.getBytes(charset));
	}

	@Override
	public ByteBuffer encodeValue(Serializable value) {
		if (value == null) return EMPTY;

		ObjectOutputStream objectOutputStream = null;
		ByteArrayOutputStream byteArrayOutputStream = null;

		try {
			byteArrayOutputStream = new ByteArrayOutputStream();
			objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
			objectOutputStream.writeObject(value);
			objectOutputStream.flush();

			return ByteBuffer.wrap(byteArrayOutputStream.toByteArray());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			CloseableUtil.close(objectOutputStream);
			CloseableUtil.close(byteArrayOutputStream);
		}

		return EMPTY;
	}

	private byte[] getBytes(ByteBuffer bytes) {
		return ByteBufUtil.getBytes(Unpooled.wrappedBuffer(bytes));
	}

}
